#include "shader_directx9.h"
#include "renderer_directx9.h"
#include <stdio.h>

namespace gecko
{

Shader_DirectX9::Shader_DirectX9(unsigned int numParameters)
: m_Effect(NULL)
, m_Parameters(NULL)
{
	if ( numParameters )
	{
		m_Parameters = new D3DXHANDLE[numParameters];
	}
}

Shader_DirectX9::~Shader_DirectX9()
{
}

bool Shader_DirectX9::Serialise( IRenderer* r, const char* filename )
{
	m_renderer = static_cast<Renderer_DirectX9*>(r);
	LPDIRECT3DDEVICE9 device = m_renderer->GetD3DDevice();

	LPD3DXBUFFER errorBuffer = NULL;

	HRESULT hr = D3DXCreateEffectFromFile(device, filename, NULL, NULL, D3DXSHADER_DEBUG, NULL, &m_Effect, &errorBuffer);

	if (FAILED(hr))
	{
		if (errorBuffer)
		{
			Error((char*)errorBuffer->GetBufferPointer());
		}

		return false;
	}

	sprintf(m_FileName, "%s", filename);

	// add to renderer's managed resource pool in event of lost device
//	m_renderer->AddShader(this);

	return true;
}

bool Shader_DirectX9::BindParameter(unsigned int parameter, const char* parameterName)
{
	D3DXHANDLE paramHandle = m_Effect->GetParameterByName(0, parameterName);

	if ( paramHandle )
	{
		m_Parameters[parameter] = paramHandle;
		return true;
	}

	return false;
}

void Shader_DirectX9::Destroy()
{
    if ( m_Effect )
    {
        m_Effect->Release();
        m_Effect = NULL;
    }

	if ( m_Parameters )
	{
		delete [] m_Parameters;
	}
}

void Shader_DirectX9::SetTechnique(const char* techName)
{
    D3DXHANDLE handle = m_Effect->GetTechniqueByName(techName);

    HRESULT hr = m_Effect->SetTechnique(handle);

    if ( FAILED(hr) )
    {
        Error("Technique name not found.");
    }
}

void Shader_DirectX9::Begin()
{
    HRESULT hr = m_Effect->Begin(&m_NumPasses, 0);

    if ( FAILED(hr) )
    {
        Error("Call to Begin() failed.");
    }
}

void Shader_DirectX9::End()
{
    HRESULT hr = m_Effect->End();

    if ( FAILED(hr) )
    {
        Error("Call to End() failed.");
    }
}

void Shader_DirectX9::BeginPass(unsigned int pass)
{
    HRESULT hr = m_Effect->BeginPass(pass);

    if ( FAILED(hr) )
    {
        Error("Call to BeginPass() failed.");
    }   
}

void Shader_DirectX9::EndPass()
{
    HRESULT hr = m_Effect->EndPass();

    if ( FAILED(hr) )
    {
        Error("Call to EndPass() failed.");
    } 
}

void Shader_DirectX9::Commit()
{
    HRESULT hr = m_Effect->CommitChanges();

    if ( FAILED(hr) )
    {
       Error("Call to CommitChanges() failed.");
    }
}

void Shader_DirectX9::SetMatrix(unsigned int parameter, const Matrix44& m)
{
    HRESULT hr = m_Effect->SetMatrix(m_Parameters[parameter], (D3DXMATRIX*)&m);

    if ( FAILED(hr) )
    {
        Error("SetMatrix call failed.");
    }
}

void Shader_DirectX9::SetVector(unsigned int parameter, const Vector3f& v)
{
    HRESULT hr = m_Effect->SetValue(m_Parameters[parameter], (D3DXVECTOR3*)&v, sizeof(D3DXVECTOR3));

    if ( FAILED(hr) )
    {
        Error("SetValue call failed.");
    }
}

void Shader_DirectX9::SetInt( unsigned int parameter, int i )
{
	HRESULT hr = m_Effect->SetInt(m_Parameters[parameter], i);

	if ( FAILED(hr) )
	{
		Error("SetValue call failed.");
	}
}

void Shader_DirectX9::SetBool(unsigned int parameter, bool b)
{
	HRESULT hr = m_Effect->SetBool(m_Parameters[parameter], b);

	if ( FAILED(hr) )
	{
		Error("SetValue call failed.");
	}
}

void Shader_DirectX9::SetFloat(unsigned int parameter, float f)
{
	HRESULT hr = m_Effect->SetFloat(m_Parameters[parameter], f);

	if ( FAILED(hr) )
	{
		Error("SetValue call failed.");
	}
}

void Shader_DirectX9::SetBlendMode(unsigned int parameter, IRenderer::eBlendMode b)
{
	int blendMode = 0;

	switch (b)
	{
	case IRenderer::BM_ZERO:
		blendMode = D3DBLEND_ZERO;
		break;
	case IRenderer::BM_ONE:
		blendMode = D3DBLEND_ONE;
		break;
	case IRenderer::BM_SRCCOLOR:
		blendMode = D3DBLEND_SRCCOLOR;
		break;
	case IRenderer::BM_INVSRCCOLOR:
		blendMode = D3DBLEND_INVSRCCOLOR;
		break;
	case IRenderer::BM_SRCALPHA:
		blendMode = D3DBLEND_SRCALPHA;
		break;
	case IRenderer::BM_INVSRCALPHA:
		blendMode = D3DBLEND_INVSRCALPHA;
		break;
	case IRenderer::BM_DESTALPHA:
		blendMode = D3DBLEND_DESTALPHA;
		break;
	case IRenderer::BM_INVDESTALPHA:
		blendMode = D3DBLEND_INVDESTALPHA;
		break;
	case IRenderer::BM_DESTCOLOR:
		blendMode = D3DBLEND_DESTCOLOR;
		break;
	case IRenderer::BM_INVDESTCOLOR:
		blendMode = D3DBLEND_INVDESTCOLOR;
		break;
	case IRenderer::BM_SRCALPHASAT:
		blendMode = D3DBLEND_SRCALPHASAT;
		break;
	}

	HRESULT hr = m_Effect->SetInt(m_Parameters[parameter], blendMode);

	if ( FAILED(hr) )
	{
		Error("SetValue call failed.");
	}
}

/* void Shader::SetTexture(ParamHandle parameter, Texture* texture)
 {
	 HRESULT hr;

	 if ( !texture )
	 {
		 hr = m_pEffect->SetTexture(m_paramHandles[parameter], NULL);
	 }
	 else
	 {
		 hr = m_pEffect->SetTexture(m_paramHandles[parameter], texture->GetTextureSurface());
	 }
 
     if ( FAILED(hr) )
     {
         gtError(Debug::MsgFilters::Renderer, "SetTexture call failed.");
     }
 }*/

void Shader_DirectX9::SetColour(unsigned int parameter, const Colour& colour)
{
    HRESULT hr = m_Effect->SetValue(m_Parameters[parameter], (D3DXCOLOR*)&colour, sizeof(D3DXCOLOR));

    if ( FAILED(hr) )
    {
        Error("SetValue call failed.");
    }
}

bool Shader_DirectX9::OnLostDevice()
{
    Destroy();
    return true;
}

bool Shader_DirectX9::OnResetDevice()
{
    LPDIRECT3DDEVICE9 device = m_renderer->GetD3DDevice();

    LPD3DXBUFFER errorBuffer = NULL;

	//FileStream stream(Stream::DIR_IN, m_fileName);
	
    if ( FAILED(D3DXCreateEffectFromFile(device, m_FileName, NULL, NULL, D3DXSHADER_DEBUG, NULL, &m_Effect, &errorBuffer)) )
    {
		if ( errorBuffer )
		{
			Error((char*)errorBuffer->GetBufferPointer());
			return false;
		}
    }

    return true;
}

void Shader_DirectX9::PostSerialise()
{

}

}